package org.yunai.swjg.server.module.player.vo;

import org.yunai.swjg.server.core.role.Role;
import org.yunai.swjg.server.core.role.RoleDef;
import org.yunai.swjg.server.core.service.Online;
import org.yunai.swjg.server.entity.PlayerEntity;
import org.yunai.swjg.server.module.currency.Currency;
import org.yunai.swjg.server.module.formation.vo.Formation;
import org.yunai.swjg.server.module.item.Inventory;
import org.yunai.swjg.server.module.item.container.Bag;
import org.yunai.swjg.server.module.item.vo.Item;
import org.yunai.swjg.server.module.partner.PartnerDiary;
import org.yunai.swjg.server.module.player.PlayerMapper;
import org.yunai.swjg.server.module.player.SexEnum;
import org.yunai.swjg.server.module.player.VocationEnum;
import org.yunai.swjg.server.module.player.template.VocationTemplate;
import org.yunai.swjg.server.module.quest.QuestDiary;
import org.yunai.swjg.server.rpc.struct.StPlayerInfo;
import org.yunai.yfserver.message.IMessage;
import org.yunai.yfserver.persistence.PersistenceObject;
import org.yunai.yfserver.spring.BeanManager;
import org.yunai.yfserver.util.ObjectUtils;

import java.util.List;

/**
 * 玩家信息
 * User: yunai
 * Date: 13-5-2
 * Time: 下午4:38
 */
public class Player extends Role implements PersistenceObject<Integer, PlayerEntity> {

    private static PlayerMapper playerMapper;

    static {
        playerMapper = BeanManager.getBean(PlayerMapper.class);
    }

    /**
     * 上一个场景ID空时的值
     */
    public static final int SCENE_PRE_ID_NULL = 0;
    /**
     * 上一个场景坐标X空时的值
     */
    public static final short SCENE_PRE_X_NULL = 0;
    /**
     * 上一个场景坐标Y空时的值
     */
    public static final short SCENE_PRE_Y_NULL = 0;

    /**
     * 在线缓存
     */
    private Online online;
    /**
     * 编号
     */
    private Integer id;
    /**
     * 目标场景X
     */
    private short toSceneX;
    /**
     * 目标场景Y
     */
    private short toSceneY;
    /**
     * 总战力
     */
    private int powerTotal;
    /**
     * 总先攻
     */
    private int xianGongTotal;
    // [begin]属于玩家的数据==============================
    /**
     * 背包清单
     */
    private final Inventory inventory;
    /**
     * 伙伴
     */
    private final PartnerDiary partnerDiary;
    /**
     * 任务信息
     */
    private final QuestDiary questDiary;
    /**
     * 阵形信息
     */
    private final Formation formation;
    // [end]属于玩家的数据================================

    @Override
    public boolean isInDB() {
        return true;
    }

    @Override
    public void setInDB(boolean inDB) {
        throw new UnsupportedOperationException("玩家基本信息肯定是在数据库中的！");
    }

    @Override
    public Integer getUnitId() {
        return id;
    }

    @Override
    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public Integer getId() {
        return id;
    }

    public Online getOnline() {
        return online;
    }

    public short getToSceneX() {
        return toSceneX;
    }

    public void setToSceneX(short toSceneX) {
        this.toSceneX = toSceneX;
    }

    public short getToSceneY() {
        return toSceneY;
    }

    public void setToSceneY(short toSceneY) {
        this.toSceneY = toSceneY;
    }

    public int getXianGongTotal() {
        return xianGongTotal;
    }

    public void setXianGongTotal(int xianGongTotal) {
        this.xianGongTotal = xianGongTotal;
    }

    public int getPowerTotal() {
        return powerTotal;
    }

    public void setPowerTotal(int powerTotal) {
        this.powerTotal = powerTotal;
    }

    public Inventory getInventory() {
        return inventory;
    }

    public PartnerDiary getPartnerDiary() {
        return partnerDiary;
    }

    public QuestDiary getQuestDiary() {
        return questDiary;
    }

    public Formation getFormation() {
        return formation;
    }

    // [业务方法BEGIN] ================================================================================================================================================================
    public Player(Online online) {
        super(RoleDef.Unit.PLAYER);
        player = this;
        this.online = online;
        online.setPlayer(this);
        this.partnerDiary = new PartnerDiary(this);
        this.inventory = new Inventory(this);
        this.questDiary = new QuestDiary(this);
        this.formation = new Formation(this);
    }

    /**
     * 加载角色所有信息
     */
    public void onLoad() {
        // 角色基本信息
        load();
        // 伙伴信息
        partnerDiary.load();
        // 背包信息
        inventory.load();
        // 任务信息
        questDiary.load();
        // 阵形信息
        formation.load();
    }

    /**
     * 初始化角色所有信息<br />
     * 该方法在所有数据接在完毕后进行调用
     */
    public void onInit() {
        // 初始化角色基本信息
        init();
        // 初始化伙伴信息
        partnerDiary.init();
    }

    /**
     * 玩家真正登录<br />
     * 1. 记录登录时间<br />
     * 2. 发送所有信息给客户端
     * 该方法在{@link #onLoad()}和{@link #onInit()}之后调用
     */
    public void onLogin() {
        // 玩家登录
        login();
        // 登录后输出角色所有信息
        onNoticeInfo();
    }

    /**
     * 发送角色所有信息
     */
    private void onNoticeInfo() {
        // 发送角色信息
        noticeInfo();
        // 发送宠物信息
        partnerDiary.noticeInfo();
        // 背包信息输出
        inventory.noticeInfo();
        // 发送任务信息
        questDiary.noticeInfo();
        // TODO 阵形
    }

    /**
     * 玩家登出
     */
    public void onLogout() {
        // 玩家登出
        logout();
    }

    private void init() {
        // 计算属性
        super.calcRoleProps(false);
    }

    /**
     * 加载玩家数据
     */
    private void load() {
        PlayerEntity entity = playerMapper.selectPlayerByUidAndServerId(online.getUser().getId(), online.getServerId());
        fromEntity(entity);
    }

    /**
     * 发送玩家基本信息
     */
    private void noticeInfo() {
        super.noticeChangedProps(false);
        super.noticeProps();
    }

    /**
     * 发送消息
     *
     * @param msg 消息
     */
    public void message(IMessage msg) {
        online.write(msg);
    }

    /**
     * 保存数据.新增和修改都算保存.
     *
     * @param po 持久化对象
     */
    public void save(PersistenceObject po) {
        online.getDataUpdater().addSave(po);
    }

    /**
     * 删除数据.
     * @param po 持久化对象
     */
    public void delete(PersistenceObject po) {
        online.getDataUpdater().addDelete(po);
    }

    @Override
    public void fromEntity(PlayerEntity entity) {
        id = entity.getId();
        // 角色字符串属性
        super.setNickname(entity.getNickname());
        super.setMedicineStrength(entity.getMedicineStrength());
        super.setMedicineStunt(entity.getMedicineStunt());
        super.setMedicineMagic(entity.getMedicineMagic());
        // 角色一级属性
        setVocation(VocationEnum.valueOf(entity.getVocation()));
        setSex(SexEnum.valueOf(entity.getSex()));
        setSceneId(entity.getSceneId());
        setSceneX(entity.getSceneX());
        setSceneY(entity.getSceneY());
        setScenePreId(entity.getScenePreId());
        setScenePreX(entity.getScenePreX());
        setScenePreY(entity.getScenePreY());
        setLevel(entity.getLevel());
        setExp(entity.getExp());
        setCoin(entity.getCoin());
        setGold(entity.getGold());
        setReputation(entity.getReputation());
        setSoul(entity.getSoul());
        setDevelopStrength(entity.getDevelopStrength());
        setDevelopStunt(entity.getDevelopStunt());
        setDevelopMagic(entity.getDevelopMagic());
        setSpecialSkill(entity.getSpecialSkill());
        // 角色不发送给客户端属性
        setLastLoginTime(entity.getLastLoginTime());
        setLastLogoutTime(entity.getLastLogoutTime());
        setLastIp(entity.getLastIp());
        setCreateRoleTime(entity.getCreateRoleTime());
        setTotalOnlineTime(entity.getTotalOnlineTime());
        setLastLastLoginTime(entity.getLastLastLoginTime());
        setLastLastIp(entity.getLastLastIp());
    }

    @Override
    public PlayerEntity toEntity() {
        PlayerEntity entity = new PlayerEntity();
        entity.setId(getId());
        entity.setLastLastLoginTime(getLastLoginTime());
        entity.setLastLastIp(getLastIP());
        entity.setLastLoginTime(System.currentTimeMillis());
        entity.setLastIp(getLastIP());
        entity.setLastLogoutTime(getLastLogoutTime());
        entity.setTotalOnlineTime(getTotalOnlineTime());
        entity.setSceneId(getSceneId());
        entity.setSceneX(getSceneX());
        entity.setSceneY(getSceneY());
        entity.setScenePreId(getScenePreId());
        entity.setScenePreX(getScenePreX());
        entity.setScenePreY(getScenePreY());
        entity.setLevel(getLevel());
        entity.setExp(getExp());
        entity.setCoin(getCoin());
        entity.setGold(getGold());
        // reputation
        // soul
        // TODO 培养属性
        entity.setSpecialSkill(getSpecialSkill());
        entity.setMedicineStrength(getMedicineStrength());
        entity.setMedicineStunt(getMedicineStunt());
        entity.setMedicineMagic(getMedicineMagic());
        return entity;
    }

    /**
     * 玩家登录<br />
     * <pre>
     *     1. 记录登录时间/IP
     *     2. 设置目标点就是角色所在点
     *     3. 计算二级属性(最后需要清理属性改变标记)
     * </pre>
     */
    public void login() {
        // 记录登录
        logLogin();
        // 设置目标点就是角色所在点
        setToSceneX(super.getSceneX());
        setToSceneY(super.getSceneY());

        // TODO 每日登录
        // TODO 新手奖励
    }

    /**
     * 玩家登录<br />
     * <pre>
     *     1. 记录登出时间+在线时间
     * </pre>
     */
    public void logout() {
        // 记录登出+在线时间
        this.logLogout();
    }

    /**
     * 记录登录时间/IP
     */
    private void logLogin() {
        this.setLastLastLoginTime(this.getLastLoginTime());
        this.setLastLastIp(this.getLastIP());
        this.setLastLoginTime(System.currentTimeMillis());
        this.setLastIp(this.getOnline().getIp());
        save(this);
    }

    /**
     * 记录登出时间/计算在线时间
     */
    private void logLogout() {
        this.setLastLogoutTime(System.currentTimeMillis());
        this.setTotalOnlineTime(getTotalOnlineTime() + (getLastLogoutTime() - getLastLoginTime()));
        save(this);
    }

    /**
     * 移动玩家的场景坐标并设置目标场景坐标<br />
     * 该方法坐标改变不再进行入库，因为更新比较频繁
     *
     * @param fromSceneX 当前场景坐标x
     * @param fromSceneY 当前场景坐标y
     * @param toSceneX   目标场景坐标x
     * @param toSceneY   目标场景坐标y
     */
    public void moveSceneXY(Short fromSceneX, Short fromSceneY, Short toSceneX, Short toSceneY) {
        setSceneX(fromSceneX);
        setSceneY(fromSceneY);
        setToSceneX(toSceneX);
        setToSceneY(toSceneY);
    }

    /**
     * 修改玩家所在场景，并入库
     *
     * @param sceneId 场景编号
     * @param sceneX  场景坐标X
     * @param sceneY  场景坐标Y
     */
    public void enterScene(Integer sceneId, Short sceneX, Short sceneY) {
        if (ObjectUtils.isEqual(sceneId, getSceneId())) {
            return;
        }
        // 设置当前场景数据
        setSceneId(sceneId);
        setSceneX(sceneX);
        setSceneY(sceneY);
        // 入库
        save(this);
    }

    /**
     * 记录当前场景信息到上一次场景信息中<br />
     * 该方法主要用于进入副本时使用
     */
    public void markScenePreInfo() {
        setScenePreId(getSceneId());
        setScenePreX(getSceneX());
        setScenePreY(getSceneY());
    }

    /**
     * 清理掉上一次场景的信息，并入库
     */
    public void clearScenePreInfo() {
        if (isScenePreInfoNull()) {
            return;
        }
        setScenePreId(SCENE_PRE_ID_NULL);
        setScenePreX(SCENE_PRE_X_NULL);
        setScenePreY(SCENE_PRE_Y_NULL);
        save(this);
    }

    /**
     * @return 上一次场景信息是否为空
     */
    public boolean isScenePreInfoNull() {
        return getScenePreId() == SCENE_PRE_ID_NULL &&
                getSceneX() == SCENE_PRE_X_NULL &&
                getSceneY() == SCENE_PRE_Y_NULL;
    }

    // TODO 未完成
    private void calcFormationProps() {
        int newPowerTotal = 0;
        int newXianGongTotal = 0;
        // TODO 循环队伍。现在暂时就加玩家。等出宠物模块
        newPowerTotal += getPower();
        newXianGongTotal += getXianGong();
    }

    /**
     * 获得玩家指定货币的数量<br />
     * 当指定货币找不到时，返回0
     *
     * @param currency 货币枚举
     * @return 货币数量
     */
    public Integer getCurrency(Currency currency) {
        switch (currency) {
            case COIN:
                return getCoin();
            case GOLD:
                return getGold();
            default:
                return 0;
        }
    }

    /**
     * 修改玩家指定货币数量<br />
     * <pre>
     *     1. 入库
     *     2. 提示客户端数据变化
     * </pre>
     *
     * @param currency 货币枚举
     * @param newNum   新货币数量
     */
    public void changeCurrency(Currency currency, int newNum) {
        switch (currency) {
            case COIN:
                setCoin(newNum);
                break;
            case GOLD:
                setGold(newNum);
                break;
        }
        save(this);
        noticeChangedProps(true);
    }

    /**
     * @return StPlayerInfo 玩家信息结构体
     */
    public StPlayerInfo toStPlayerInfo() {
        return new StPlayerInfo(this.getId(), this.getLevel(), this.getNickname(), this.getSceneX(), this.getSceneY(),
                this.getToSceneX(), this.getToSceneY());
    }

    @Override
    protected List<Item> getEquipments() {
        return inventory.getBag(getId(), Bag.BagType.EQUIPMENT).getAll();
    }

    @Override
    protected BaseProperties getProperties() {
        VocationTemplate template = VocationTemplate.get(getVocation());
        return new BaseProperties(template.getBaseStrength(), template.getBaseStunt(),
                template.getBaseMagic(), template.getBaseHp());
    }

    @Override
    protected void onModified() {
        save(this);
    }

    /**
     * @param roleId 角色编号（伙伴编号或者玩家编号）
     * @return 玩家/伙伴
     */
    public Role getRole(int roleId) {
        return roleId == getId() || roleId == 0 ? this : partnerDiary.get(roleId);
    }
}
